﻿namespace MicroCloud.Entity.BatchConfiguration
{
    /// <summary>
    /// 配置实体属性成员公共规则
    /// </summary>
    public class PropertyPublicConfiguration : IEntityBatchConfiguration
    {
        //字段
        private readonly IServiceProvider _provider;
        private readonly ILogger _logger;

        /// <summary>
        /// 初始化一个 <see cref="PropertyPublicConfiguration"/> 类型的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        public PropertyPublicConfiguration(IServiceProvider provider)
        {
            _provider = provider;
            _logger = provider.GetLogger(this);

        }

        /// <summary>
        /// 配置指定的 <see cref="IMutableEntityType"/>
        /// </summary>
        /// <param name="modelBuilder">模型构建器</param>
        /// <param name="mutableEntityType">实体的 <see cref="IMutableEntityType"/> 类型</param>
        /// <param name="database">数据库相关信息和操作的访问 <see cref="DatabaseFacade"/> 类型</param>
        /// <param name="dbContextOptions">数据上下文配置 <see cref="DbContextOption"/> 类型</param>
        /// <param name="dbContextType">数据上下文类型</param>
        public void Configure(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType, DatabaseFacade database, DbContextOption dbContextOptions, Type dbContextType)
        {
            IMutableProperty[] mutableProperties = mutableEntityType.GetProperties().ToArray();
            foreach (IMutableProperty mutableProperty in mutableProperties)
            {
                if (mutableProperty.PropertyInfo == null)
                {
                    continue;
                }

                var propertyType = mutableProperty.PropertyInfo.PropertyType;
                var propertyBuilder = modelBuilder.Entity(mutableEntityType.ClrType).Property(mutableProperty.PropertyInfo.Name);

                // 数据库类型
                var databaseType = dbContextOptions?.DatabaseType;
                if (databaseType == null)
                {
                    var databaseProviderName = database.ProviderName.ToLower();
                    if (databaseProviderName.Contains("sqlserver")) { databaseType = DatabaseType.SqlServer; }
                    else if (databaseProviderName.Contains("sqlite")) { databaseType = DatabaseType.Sqlite; }
                    else if (databaseProviderName.Contains("mysql")) { databaseType = DatabaseType.MySql; }
                    else if (databaseProviderName.Contains("postgresql")) { databaseType = DatabaseType.PostgreSql; }
                    else if (databaseProviderName.Contains("oracle")) { databaseType = DatabaseType.Oracle; }
                }

                #region "配置数据库字段值生成规则"
                if (typeof(int).Equals(propertyType) || typeof(long).Equals(propertyType))
                {
                    var fieldGeneratedAttribute = mutableProperty.PropertyInfo.GetAttribute<ValueGenerateAttribute>();
                    if (fieldGeneratedAttribute != null)
                    {
                        switch (fieldGeneratedAttribute.GenerateRule)
                        {
                            case ValueGenerateRule.Never:
                                //将属性配置为在保存此实体类型的实例时从不具有数据库生成的值。
                                propertyBuilder.ValueGeneratedNever();
                                break;
                            case ValueGenerateRule.OnAdd:
                                //将属性配置为仅在保存新实体时生成值，除非设置了非空的非临时值，在这种情况下，将改为保存设置的值。
                                //该值可以由客户端值生成器生成，也可以由数据库生成，作为保存实体的一部分。
                                propertyBuilder.ValueGeneratedOnAdd();
                                break;
                            case ValueGenerateRule.OnAddOrUpdate:
                                //将属性配置为在保存新实体或现有实体时生成值。
                                propertyBuilder.ValueGeneratedOnAddOrUpdate();
                                break;
                            case ValueGenerateRule.OnUpdate:
                                //将属性配置为在保存现有实体时生成值。
                                propertyBuilder.ValueGeneratedOnUpdate();
                                break;
                            case ValueGenerateRule.OnUpdateSometimes:
                                //将属性配置为在保存现有实体时在特定条件下生成的值。
                                propertyBuilder.ValueGeneratedOnUpdateSometimes();
                                break;
                        }
                    }
                }
                #endregion
                #region "配置 System.String 属性的长度"
                if (typeof(string).Equals(propertyType))
                {
                    var stringMaxAttribute = mutableProperty.PropertyInfo.GetAttribute<StringMaxAttribute>();

                    var stringLengthAttribute = mutableProperty.PropertyInfo.GetAttribute<StringLengthAttribute>();
                    var maxLengthAttribute = mutableProperty.PropertyInfo.GetAttribute<MaxLengthAttribute>();
                    var maxLength1 = stringLengthAttribute == null ? 0 : stringLengthAttribute.MaximumLength;
                    var maxLength2 = maxLengthAttribute == null ? 0 : maxLengthAttribute.Length;
                    var maxLength = maxLength1 > maxLength2 ? maxLength1 : maxLength2;

                    if (maxLength <= 0 && stringMaxAttribute == null)
                    {
                        //默认长度：常规类型 - 512
                        propertyBuilder.HasMaxLength((int)StringSizeType.General);
                    }
                    else
                    {
                        switch (databaseType)
                        {
                            case DatabaseType.SqlServer:
                                if (maxLength > 0 && maxLength <= 4000)
                                {
                                    propertyBuilder.HasColumnType($"nvarchar({maxLength})");
                                }
                                else if (maxLength > 4000)
                                {
                                    propertyBuilder.HasColumnType($"nvarchar(MAX)");
                                }
                                break;
                            case DatabaseType.Sqlite:
                                if (maxLength > 0 && maxLength <= 4000)
                                {
                                    propertyBuilder.HasColumnType($"NVARCHAR({maxLength})");
                                }
                                else if (maxLength > 4000)
                                {
                                    propertyBuilder.HasColumnType($"NTEXT");
                                }
                                break;
                            case DatabaseType.MySql:
                                if (maxLength > 0 && maxLength <= 255)
                                {
                                    propertyBuilder.HasColumnType($"VARCHAR({maxLength})");
                                }
                                else if (maxLength > 255 && maxLength <= 4000)
                                {
                                    propertyBuilder.HasColumnType($"VARCHAR({maxLength})");
                                }
                                else if (maxLength > 4000 && maxLength <= 65535)
                                {
                                    propertyBuilder.HasColumnType($"TEXT");
                                }
                                else if (maxLength > 65535 && maxLength <= 16777215)
                                {
                                    propertyBuilder.HasColumnType($"MEDIUMTEXT");
                                }
                                else if (maxLength > 16777215)
                                {
                                    propertyBuilder.HasColumnType($"LONGTEXT");
                                }
                                break;
                            //case DatabaseType.PostgreSql:
                            //    if (maxLength > 0 && maxLength <= 255)
                            //    {
                            //        propertyBuilder.HasColumnType($"varchar({maxLength})");
                            //    }
                            //    break;
                            case DatabaseType.Oracle:
                                if (maxLength > 0 && maxLength <= 4000)
                                {
                                    propertyBuilder.HasColumnType($"NVARCHAR2({maxLength})");
                                }
                                else if (maxLength > 4000)
                                {
                                    propertyBuilder.HasColumnType($"NCLOB");
                                }
                                break;
                        }
                    }
                    propertyBuilder.IsUnicode();
                    //设置字符属性数据库长文本类型映射
                    //if (stringMaxAttribute?.IsText == true)
                    //{
                    //    switch (databaseType)
                    //    {
                    //        case DatabaseType.SqlServer:
                    //            propertyBuilder.HasColumnType("ntext");
                    //            break;
                    //        case DatabaseType.Sqlite:
                    //            propertyBuilder.HasColumnType("NTEXT");
                    //            break;
                    //        case DatabaseType.MySql:
                    //            propertyBuilder.HasColumnType("LONGTEXT");
                    //            break;
                    //        case DatabaseType.PostgreSql:
                    //        case DatabaseType.Kdbndp:
                    //            propertyBuilder.HasColumnType("text");
                    //            break;
                    //        case DatabaseType.Oracle:
                    //            propertyBuilder.HasColumnType("NCLOB");
                    //            break;
                    //    }
                    //    ////设置最大长度
                    //    //propertyBuilder.HasMaxLength(int.MaxValue);
                    //}
                    //propertyBuilder.HasColumnType($"nvarchar({_stringKeyDefaultLength})");
                }
                #endregion
                #region "配置 System.Decimal 属性的精度和小数位数"
                else if (typeof(decimal).Equals(propertyType) || typeof(decimal?).Equals(propertyType))
                {
                    var decimalPrecision = DataTableConstants.DecimalDefaultPrecision;
                    var decimalScale = DataTableConstants.DecimalDefaultScale;
                    var decimalPrecisionAttribute = mutableProperty.PropertyInfo.GetAttribute<DecimalPrecisionAttribute>();
                    if (decimalPrecisionAttribute != null)
                    {
                        if (decimalPrecisionAttribute.Precision < DataTableConstants.DecimalMinPrecision || decimalPrecisionAttribute.Precision > DataTableConstants.DecimalMaxPrecision)
                        {
                            //throw new InvalidOperationException($"精度(Precision) {decimalPrecisionAttribute.Precision} 必须介于 {DataTableConstants.DecimalMinPrecision} 和 {DataTableConstants.DecimalMaxPrecision} 之间");
                            throw new InvalidOperationException(I18N.T("精度(Precision) {0} 必须介于 {1} 和 {2} 之间", decimalPrecisionAttribute.Precision, DataTableConstants.DecimalMinPrecision, DataTableConstants.DecimalMaxPrecision));
                        }
                        if (decimalPrecisionAttribute.Scale < DataTableConstants.DecimalMinScale || decimalPrecisionAttribute.Scale > decimalPrecisionAttribute.Precision)
                        {
                            //throw new InvalidOperationException($"刻度(Scale) {decimalPrecisionAttribute.Scale} 必须介于 {DataTableConstants.DecimalMinScale} 和精度值(Precision) {decimalPrecisionAttribute.Precision} 之间");
                            throw new InvalidOperationException(I18N.T("刻度(Scale) {0} 必须介于 {1} 和精度值(Precision) {2} 之间", decimalPrecisionAttribute.Scale, DataTableConstants.DecimalMinScale, decimalPrecisionAttribute.Precision));
                        }
                        decimalPrecision = decimalPrecisionAttribute.Precision;
                        decimalScale = decimalPrecisionAttribute.Scale;
                    }
                    propertyBuilder.HasPrecision(decimalPrecision, decimalScale);
                    //propertyBuilder.HasColumnType($"decimal({precision},{scale})");
                }
                #endregion

            }

        }

    }
}
